{% macro construct(property, source) %}
def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_string() }}:
    {% if "None" in property.get_type_strings_in_union(json=True) %}
    if data is None:
        return data
    {% endif %}
    {% if "Unset" in property.get_type_strings_in_union(json=True) %}
    if isinstance(data, Unset):
        return data
    {% endif %}
    {% set ns = namespace(contains_unmodified_properties = false) %}
    {% for inner_property in property.inner_properties %}
    {% import "property_templates/" + inner_property.template as inner_template %}
        {% if not inner_template.construct %}
            {% set ns.contains_unmodified_properties = true %}
            {% continue %}
        {% endif %}
    {% if inner_template.check_type_for_construct and (not loop.last or ns.contains_unmodified_properties) %}
    try:
        if not {{ inner_template.check_type_for_construct(inner_property, "data") }}:
            raise TypeError()
        {{ inner_template.construct(inner_property, "data") | indent(8) }}
        return {{ inner_property.python_name }}
    except (TypeError, ValueError, AttributeError, KeyError):
        pass
    {% else  %}{# Don't do try/except for the last one nor any properties with no type checking #}
    {% if inner_template.check_type_for_construct %}
    if not {{ inner_template.check_type_for_construct(inner_property, "data") }}:
        raise TypeError()
    {% endif %}
    {{ inner_template.construct(inner_property, "data") | indent(4) }}
    return {{ inner_property.python_name }}
    {% endif %}
    {% endfor %}
    {% if ns.contains_unmodified_properties %}
    return cast({{ property.get_type_string() }}, data)
    {% endif %}

{{ property.python_name }} = _parse_{{ property.python_name }}({{ source }})
{% endmacro %}

{% macro transform(property, source, destination, declare_type=True, skip_unset=False) %}
{% set ns = namespace(contains_properties_without_transform = false, contains_modified_properties = not property.required, has_if = false) %}
{% if declare_type %}{{ destination }}: {{ property.get_type_string(json=True) }}{% endif %}

{% if not property.required and not skip_unset %}
if isinstance({{ source }}, Unset):
    {{ destination }} = UNSET
    {% set ns.has_if = true %}
{% endif %}
{% for inner_property in property.inner_properties %}
    {% import "property_templates/" + inner_property.template as inner_template %}
    {% if not inner_template.transform %}
        {% set ns.contains_properties_without_transform = true %}
        {% continue %}
    {% else %}
        {% set ns.contains_modified_properties = true %}
    {% endif %}
    {% if not ns.has_if %}
if isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
        {% set ns.has_if = true %}
    {% elif not loop.last or ns.contains_properties_without_transform %}
elif isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
    {% else %}
else:
    {% endif %}
    {{ inner_template.transform(inner_property, source, destination, declare_type=False) | indent(4) }}
{% endfor %}
{% if ns.contains_properties_without_transform and ns.contains_modified_properties %}
else:
    {{ destination }} = {{ source }}
{%- elif ns.contains_properties_without_transform %}
{{ destination }} = {{ source }}
{%- endif %}
{% endmacro %}


{% macro instance_check(inner_property, source) %}
{% if inner_property.get_instance_type_string() == "None" %}
if {{ source }} is None:
{% else %}
if isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
{% endif %}
{% endmacro %}

{% macro multipart(property, source, destination) %}
{% set ns = namespace(has_if = false) %}
{% for inner_property in property.inner_properties %}
{% if not ns.has_if %}
{{ instance_check(inner_property, source) }}
{% set ns.has_if = true %}
{% elif not loop.last %}

el{{ instance_check(inner_property, source) }}
{% else %}

else:
{% endif %}
{% import "property_templates/" + inner_property.template as inner_template %}
    {{ inner_template.multipart(inner_property, source, destination) | indent(4) | trim }}
{%- endfor -%}
{% endmacro %}
